/*____________________________________________________________________________*/

#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>

#include "auxiliaire.h"
#include "types.h"
#include <pthread.h>


#define LOG_FILE "../LOG.txt"
#define COMBO_INSTRUCTIONS 10

pthread_mutex_t verrouOS = PTHREAD_MUTEX_INITIALIZER;

/*!
 *
 * \brief quand le OS reçoit une demande d'accès à la mémoire de PROC[k],
 * lancer processus MMU pout interroger le numéro du cadre associé
 */


void executer_mmu( int npage, int ncadre)

{

    char page[BUFFER_SIZE];
    char cadre[BUFFER_SIZE];
    char ligne[256];
    int i,pid;
    Mmu  mmu;
    int cadre;

    proc=proc_encours;
    sprintf(page,"%d",npage);
    sprintf(cadre,"%d",ncadre);
    pid = ouvreProc( &(mmu->tuyau[Ecriture]), &(mmu->tuyau[Lecture]),
    		"mmu", page, cadre);

    /*si la table de TLB est trouvée afficher le numéro du cadre*/
          fprintf(mmu->tuyau[Lecture],"PT %d\n",npage);
          fgets(ligne, sizeof(ligne), mmu->tuyau[Ecriture]);
          cadre=atoi(ligne[0];
          if(cadre!=-1){
    	  printf("la table de TLB est trouvé *_*\n" );
          fprintf(d->log,"[os] le numéro du cadre associé est trouvé %s ",ligne);
          return cadre;
          }
}




/*____________________________________________________________________________*/

int creerProcessus( Donnees *d ){

	char buffer[BUFFER_SIZE];
	myPS *proc;

	proc = (myPS*) malloc(sizeof(myPS));

	sprintf(buffer,"%d",d->pagesParProc);
    proc->pid = ouvreProc( &(proc->tuyau[Ecriture]), &(proc->tuyau[Lecture]),
    		               "proc", buffer, NULL);
    proc->nb_instruction = 0;
    creation_table_page( &(proc->page_table), d->pagesParProc);

	printf(">> Le numéro du processus crée est %d\n", proc->pid);
	fprintf(d->log,"[os] Processus %d crée.\n",proc->pid);

	proc->suivant = d->tableProc.tete;
	d->tableProc.tete = proc;

	return proc->pid;
}

/*____________________________________________________________________________*/

void killProcessus(Donnees *d){

	int pos,i;
	myPS *proc;

	proc = d->tableProc.encours;
	pos = position(proc->pid, d->tuer, d->nTuer);

	if(d->detruire || pos>=0){

		/* Tue le processus */
		fprintf(proc->tuyau[Lecture],"KILL\n");
		fflush(proc->tuyau[Lecture]);
		sleep(1);

		/* L'enleve de la liste de processus */
		if(d->tableProc.tete == proc){
			d->tableProc.tete = proc->suivant;
		    d->tableCadres.is_bound=faulse;
		}
		else{
			myPS *procAv;
			procAv = d->tableProc.tete;
			while(procAv->suivant != proc)
				procAv = procAv->suivant;

			procAv->suivant = proc->suivant;
		}

		/* Enlève le processus de la liste <<tuer>> */
		enleve(pos,d->tuer,d->nTuer);
		d->nTuer -= (pos>=0) ? 1 : 0;
        d->tableCadres.is_bound=0;
		fprintf(d->log,"[OS] Processus %d détruit.\n",proc->pid);

		d->tableProc.encours = proc->suivant;

		/* quand on détruit un processus on fermer les tuyaux et librer l'espace il occupe */
		fclose(proc->tuyau[Ecriture]);
		fclose(proc->tuyau[Lecture]);
		free(proc->page_table.pte);
		free(proc);
	}
	    fprintf(proc->tuyau[Lecture],"STOP\n");
	    fflush(proc->tuyau[Lecture]);

	    d->tableProc.encours = proc->suivant;

}



void traiteProc(Donnees *d){
	        int i,npage,ncadre;
			char buffer[BUFFER_SIZE];
		    char mode;
		    int cadre_victime;
		    myPS *proc;

		    proc = d->tableProc.encours;
	        fprintf(proc->tuyau[Lecture],"RUN\n");
	        fflush(proc->tuyau[Lecture]);

	        fprintf(d->log,"[OS] RUN au Proc %d\n",proc->pid);

			for(i=0; i<COMBO_INSTRUCTIONS; i++){
				fgets(buffer, BUFFER_SIZE, proc->tuyau[Ecriture]);
				fprintf(d->log,"[PROC %d] %s",proc->pid,buffer);
				(proc->nb_instruction)++;
                mode=buffer[0];
                npage=atoi(buffer[1]);
                if(executer_mmu(npage,ncadre)==-1)/* la réponse du MMU est -1,trois situation doit étre traiter*/
                {
                	if(proc->page_table.pte.valid=="1") {/*entrée existe mais absente du TLB*/
                	for(i=0;i<=ncadre;i++)
                	fprintf(mmu->tuyau[Lecture],"ADD %d\t%d\n",npage,i);
                	fgets(buffer, BUFFER_SIZE, mmu->tuyau[Ecriture]);
                	if(strncmp(buffer,"OK",2)!=NULL )
                	{
                	      mmu->page_table.pte.frame_num=i;
                	      mmu->page_table.pte.valid=1;
                	      if(mode=="w")  {mmu->page_table.pte.valid="1";}
                	      else {mmu->page_table.pte.valid="1";}
                	 }

                	}/* entrée existe et elle marqué invalide*/

                	else{
                		cadre_victime = rand()%ncadre;

                	}


			}

			fprintf(proc->tuyau[Lecture],"STOP\n");
			fflush(proc->tuyau[Lecture]);

			d->tableProc.encours = proc->suivant;
		}


}

/*____________________________________________________________________________*/

void* traiterDonnees( void *donnees ){

	Donnees *d = (Donnees*) donnees;



	while(1){

		if(d->detruire && d->tableProc.tete==NULL){
			/*fprintf(d->MMU_in,"KILL");*/
			free(d->tableCadres.fte);
			fprintf(d->log,"[os] Fini de détruire les processus.\n");
			fclose(d->log);
			exit(EXIT_SUCCESS);
		}

		if(d->nCreer > 0){
			creerProcessus(d);
			(d->nCreer)--;
			continue;
		}

		if(d->tableProc.encours !=NULL){
			executerProcessus(d);
		}
		else if(d->tableProc.tete != NULL){
			d->tableProc.encours = d->tableProc.tete;

			/* Faire une pause à la fin de la queue de processus */
			sleep(1);
		}

	}
	return NULL;
}

/*____________________________________________________________________________*/

void* traiterDemandes(void *donnees) {

	Donnees *d = (Donnees*) donnees;

	while (d->detruire == False) {

		cmd_os cmd;
		myPS *proc;
		char commande[BUFFER_SIZE];
		int outval1 =-1, outval2 = -1, retval;

		/* Lit la demande d'utilisateur */
		retval = read_and_parse(stdin, commande, &outval1, &outval2);
		if (retval <= 0) {
			fprintf(stderr, "<< Demande invalide >>\n");
			continue;
		}
		cmd = str2cmd_os(commande);

		switch (cmd) {

		/* Créer un processus */
		case InCreate:
			pthread_mutex_lock(&verrouOS);
			(d->nCreer)++;
			pthread_mutex_unlock(&verrouOS);
			break;

		/* Lister les processus */
		case InListe:
			printf("\n\tNuméro du Processus\t Nombre d'instructions\n");
			proc = d->tableProc.tete;

			if(proc==NULL) printf("\t%11d\t\t%12d\n", 0, 0);
			while(proc != NULL) {
				/* Imprime les processus de façon à bien les visualiser */
				printf("\t%11d\t\t%12d\n", proc->pid, proc->nb_instruction);
				proc = proc->suivant;
			}
			break;

		/* La fin du processus PROC[k] d'identifiant n */
		case InEnd:
			if(outval1<0) {
				fprintf(stderr, "<< Demande invalide >>\n");
				continue;
			}
			pthread_mutex_lock(&verrouOS);
			d->tuer[d->nTuer] = outval1;
			(d->nTuer)++;
			pthread_mutex_unlock(&verrouOS);
			break;

		/* La fin du processus OS et destruction propre de ses processus fils */
		case InKill_os:
			pthread_mutex_lock(&verrouOS);
			d->detruire = True;
			pthread_mutex_unlock(&verrouOS);
			break;

		default:
			fprintf(stderr,"<< demande invalide >>");
			break;
		}
	}

	return NULL;
}

/*____________________________________________________________________________*/

int main(int argc, char *argv[]) {

	Donnees d;

	pthread_t th1;
	pthread_t th2;

	initDonnees(&d,10, 30);
	/*create_mmu(10,30,&d);*/
	/*d.nCreer = 0;*/

	pthread_create( &th1, NULL,traiterDemandes, (void*) &d);/*créer une fonction*/
	pthread_create( &th2, NULL,traiterDonnees, (void*) &d);


	pthread_join(th1,NULL);/*attendre la fin de thread:th1*/
	pthread_join(th2,NULL);/*attendre la fin de thread:th2*/

	exit(EXIT_FAILURE);
}

/*____________________________________________________________________________*/
